#pragma once

#include <windows.h>

#define WM_MOUSEHWHEEL                  0x020E
#define WM_XBUTTONDBLCLK                0x020D
#define WM_XBUTTONDOWN                  0x020B
#define WM_XBUTTONUP                    0x020C
#define WM_UNICHAR                      0x0109
#define MK_XBUTTON1                     0x0020
#define MK_XBUTTON2                     0x0040

#define READ(name, type) type Get##name() const { return name; }
#define WRITE(name, type) void Set##name(const (type)& _value) { name = _value; }
#define RW(name, type) READ(name, type) WRITE(name, type) 

enum KEY_STATE {KS_UP, KS_DOWN};
enum KEY_PROPERTY {KP_VIRTUALKEY, KP_CHARCODE};

typedef struct s_event_key {
  int Id;
  bool Alt;
  KEY_STATE State;
  KEY_PROPERTY Property;
  union {
    LPARAM lParam;
    struct {
      unsigned lpRepeatCount : 16;
      unsigned lpScanCode : 8;
      unsigned lpExtended : 1;
      unsigned lpReserved : 4;
      unsigned lpContext : 1;
      unsigned lpPrev : 1;
      unsigned lpTrans : 1;
    };
  };  	
} t_event_key, *p_event_key;

enum MOUSE_STATE {MS_MOVE, MS_UP, MS_DOWN, MS_DBLCLK, MS_WHEEL, MS_HWHEEL};
enum MOUSE_CONTROL {MC_CTRL, MC_LBTN, MC_MBTN, MC_RBTN, MC_SHIFT, MC_X1BTN, MC_X2BTN};
const int MOUSE_CONTROL_CONST[7] = {MK_CONTROL, MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_SHIFT, 
MK_XBUTTON1, MK_XBUTTON2};
const MOUSE_STATE MOUSE_STATE_CONST[15] = {MS_MOVE, MS_DOWN, MS_UP, MS_DBLCLK, MS_DOWN, 
  MS_UP, MS_DBLCLK, MS_DOWN, MS_UP, MS_DBLCLK, MS_WHEEL, MS_DOWN, MS_UP, MS_DBLCLK, MS_HWHEEL};

typedef struct s_event_mouse {
  MOUSE_STATE State;	
  bool Controls[7];
  int X, Y, Wheel, HWheel;
} t_event_mouse, *p_event_mouse;

typedef void (*t_proc_key)(p_event_key event);
typedef void (*t_proc_mouse)(p_event_mouse event);

class Wnd
{
private:
  const DWORD ExStyle[2]; 
  const DWORD Style[2];
  WNDCLASSEX Cls;
  bool FullScreen, Running;
  HWND Handle;
  int Width, Height, ClientWidth, ClientHeight, X, Y;
  const char* Caption;
  HDC hDC;
public:
  t_proc_key ProcKey;
  t_proc_mouse ProcMouse;
  bool RestoreMode() const { return ChangeDisplaySettings(NULL, 0); };
  bool SetMode(int _width, int _height, bool _fullscr);
  bool SetCaption(const char* _caption);
  READ(Caption, const char*);
/*  const char *GetCaption() const { return Caption; };
*/  
  // RW(Running, bool);
  bool GetRunning() { return Running; };
  void SetRunning(bool _value) { Running = _value; };

  READ(FullScreen, bool);
/*  bool GetFullScreen() const { return FullScreen; };
*/
  READ(Width, int);
  READ(Height, int);
  READ(ClientWidth, int);
  READ(ClientHeight, int);
/*  int GetWidth() const { return Width; };
  int GetHeight() const { return Height; }
  int GetClientWidth() const { return ClientWidth; };
  int GetClientHeight() const { return ClientHeight; };
*/  void Run();

  Wnd(int _width, int _height, bool _fullscr, const char* _caption,  
    t_proc_mouse _mouse, t_proc_key _key);
  ~Wnd();
};